home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tk2.3 / dist / tkGrab.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-07  |  33.5 KB  |  1,107 lines

  1. /* 
  2.  * tkGrab.c --
  3.  *
  4.  *    This file provides procedures that implement grabs for Tk.
  5.  *
  6.  * Copyright 1992 Regents of the University of California.
  7.  * Permission to use, copy, modify, and distribute this
  8.  * software and its documentation for any purpose and without
  9.  * fee is hereby granted, provided that the above copyright
  10.  * notice appear in all copies.  The University of California
  11.  * makes no representations about the suitability of this
  12.  * software for any purpose.  It is provided "as is" without
  13.  * express or implied warranty.
  14.  */
  15.  
  16. #ifndef lint
  17. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkGrab.c,v 1.18 92/08/07 09:55:31 ouster Exp $ SPRITE (Berkeley)";
  18. #endif
  19.  
  20. #include "tkConfig.h"
  21. #include "tkInt.h"
  22.  
  23. /*
  24.  *-------------------------------------------------------------------
  25.  * Problems with current grab implementation (8/7/92):
  26.  *
  27.  * 1. In a local grab the synthesized events are always placed at the
  28.  *    front of the event queue.  If there are several grabs and ungrabs
  29.  *    in a row, the groups of events for the different grabs/ungrabs
  30.  *    end up in backwards order.
  31.  * 2. The variables serverWinPtr and pointerWinPtr are hardly used at
  32.  *    all and should probably be eliminated.
  33.  * 3. The fact that grabWinPtr is set at the time a grab is set or
  34.  *    released, rather than when its events are processed, means that
  35.  *    it can get out of sync with the event queue if there's a rapid
  36.  *    sequence of grabs or ungrabs.  The only solution I can think of
  37.  *    is to keep a parallel queue to the event queue to update grabWinPtr
  38.  *    (or, synthesize an event to change the pointer?).
  39.  *-------------------------------------------------------------------
  40.  */
  41.  
  42. /*
  43.  * Bit definitions for grabFlags field of TkDisplay structures:
  44.  *
  45.  * GRAB_GLOBAL            1 means this is a global grab (we grabbed via
  46.  *                the server so all applications are locked out.
  47.  *                0 means this is a local grab that affects
  48.  *                only this application.
  49.  * GRAB_BUTTON_RELEASE        1 means that a button-release event just
  50.  *                occurred and we're in the middle of a sequence
  51.  *                of Enter and Leave events with NotifyUngrab
  52.  *                mode.
  53.  */
  54.  
  55. #define GRAB_GLOBAL        1
  56. #define GRAB_BUTTON_RELEASE    2
  57.  
  58. /*
  59.  * Forward declarations for procedures declared later in this file:
  60.  */
  61.  
  62. static void        ChangeEventWindow _ANSI_ARGS_((XEvent *eventPtr,
  63.                 TkWindow *winPtr));
  64. static void        MovePointer _ANSI_ARGS_((XEvent *eventPtr,
  65.                 TkWindow *sourcePtr, TkWindow *destPtr));
  66. static void        MovePointer2 _ANSI_ARGS_((TkWindow *sourcePtr,
  67.                 TkWindow *destPtr, int mode));
  68.  
  69. /*
  70.  *----------------------------------------------------------------------
  71.  *
  72.  * Tk_GrabCmd --
  73.  *
  74.  *    This procedure is invoked to process the "grab" Tcl command.
  75.  *    See the user documentation for details on what it does.
  76.  *
  77.  * Results:
  78.  *    A standard Tcl result.
  79.  *
  80.  * Side effects:
  81.  *    See the user documentation.
  82.  *
  83.  *----------------------------------------------------------------------
  84.  */
  85.  
  86.     /* ARGSUSED */
  87. int
  88. Tk_GrabCmd(clientData, interp, argc, argv)
  89.     ClientData clientData;    /* Main window associated with
  90.                  * interpreter. */
  91.     Tcl_Interp *interp;        /* Current interpreter. */
  92.     int argc;            /* Number of arguments. */
  93.     char **argv;        /* Argument strings. */
  94. {
  95.     TkWindow *winPtr = (TkWindow *) clientData;
  96.     int length, lockScreen;
  97.     char *window;
  98.  
  99.     if (argc > 3) {
  100.     badArgs:
  101.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  102.         argv[0], " ?-global? ?window?\"", (char *) NULL);
  103.     return TCL_ERROR;
  104.     }
  105.     if (argc == 1) {
  106.     if ((winPtr->dispPtr->grabWinPtr != NULL)
  107.         && (winPtr->dispPtr->grabWinPtr->mainPtr
  108.         == winPtr->mainPtr)) {
  109.         interp->result = Tk_PathName(winPtr->dispPtr->grabWinPtr);
  110.     } else {
  111.         interp->result = "none";
  112.     }
  113.     return TCL_OK;
  114.     }
  115.     if (argc == 3) {
  116.     length = strlen(argv[1]);
  117.     if ((strncmp(argv[1], "-global", length) != 0) || (length < 2)) {
  118.         goto badArgs;
  119.     }
  120.     lockScreen = 1;
  121.     window = argv[2];
  122.     } else {
  123.     lockScreen = 0;
  124.     window = argv[1];
  125.     }
  126.     if ((window[0] == '\0')
  127.         || (strncmp(window, "none", strlen(window)) == 0)) {
  128.     Tk_Ungrab((Tk_Window) winPtr);    
  129.     } else {
  130.     Tk_Window tkwin;
  131.  
  132.     tkwin = Tk_NameToWindow(interp, window, (Tk_Window) winPtr);
  133.     if (tkwin == NULL) {
  134.         return TCL_ERROR;
  135.     }
  136.     return Tk_Grab(interp, tkwin, lockScreen);
  137.     }
  138.     return TCL_OK;
  139. }
  140.  
  141. /*
  142.  *----------------------------------------------------------------------
  143.  *
  144.  * Tk_Grab --
  145.  *
  146.  *    Grabs the pointer and keyboard, so that mouse-related events are
  147.  *    only reported relative to a given window and its descendants.
  148.  *
  149.  * Results:
  150.  *    A standard Tcl result is returned.  TCL_OK is the normal return
  151.  *    value;  if the grab could not be set then TCL_ERROR is returned
  152.  *    and interp->result will hold an error message.
  153.  *
  154.  * Side effects:
  155.  *    Once this call completes successfully, no window outside the
  156.  *    tree rooted at tkwin will receive pointer- or keyboard-related
  157.  *    events until the next call to Tk_Ungrab.  If a previous grab was
  158.  *    in effect within this application, then it is replaced with a new
  159.  *    one.
  160.  *
  161.  *----------------------------------------------------------------------
  162.  */
  163.  
  164. int
  165. Tk_Grab(interp, tkwin, grabGlobal)
  166.     Tcl_Interp *interp;            /* Used for error reporting. */
  167.     Tk_Window tkwin;            /* Window on whose behalf the pointer
  168.                      * is to be grabbed. */
  169.     int grabGlobal;            /* Non-zero means issue a grab to the
  170.                      * server so that no other application
  171.                      * gets mouse or keyboard events.
  172.                      * Zero means the grab only applies
  173.                      * within this application. */
  174. {
  175.     int grabResult;
  176.     TkWindow *winPtr = (TkWindow *) tkwin;
  177.     TkDisplay *dispPtr = winPtr->dispPtr;
  178.     int grabRequest, inSequence, ignoring, numEvents, i, diff;
  179.     XEvent *events, *eventPtr;
  180.     TkWindow *winPtr2;
  181.  
  182.     if (dispPtr->grabWinPtr != NULL) {
  183.     if ((dispPtr->grabWinPtr == winPtr)
  184.         && (grabGlobal == ((dispPtr->grabFlags & GRAB_GLOBAL) != 0))) {
  185.         return TCL_OK;
  186.     }
  187.     if (dispPtr->grabWinPtr->mainPtr != winPtr->mainPtr) {
  188.         alreadyGrabbed:
  189.         interp->result = "grab failed: another application has grab";
  190.         return TCL_ERROR;
  191.     }
  192.     Tk_Ungrab(tkwin);
  193.     }
  194.  
  195.     if (grabGlobal) {
  196.     grabRequest = NextRequest(dispPtr->display);
  197.     grabResult = XGrabPointer(dispPtr->display, Tk_WindowId(tkwin),
  198.         True, ButtonPressMask|ButtonReleaseMask|ButtonMotionMask,
  199.         GrabModeAsync, GrabModeAsync, None, None,
  200.         TkCurrentTime(dispPtr));
  201.     if (grabResult != 0) {
  202.         grabError:
  203.         if (grabResult == GrabNotViewable) {
  204.         interp->result = "grab failed: window not viewable";
  205.         } else if (grabResult == AlreadyGrabbed) {
  206.         goto alreadyGrabbed;
  207.         } else if (grabResult == GrabFrozen) {
  208.         interp->result = "grab failed: keyboard or pointer frozen";
  209.         } else if (grabResult == GrabInvalidTime) {
  210.         interp->result = "grab failed: invalid time";
  211.         } else {
  212.         char msg[100];
  213.     
  214.         sprintf(msg, "grab failed for unknown reason (code %d)",
  215.             grabResult);
  216.         Tcl_AppendResult(interp, msg, (char *) NULL);
  217.         }
  218.         return TCL_ERROR;
  219.     }
  220.     grabResult = XGrabKeyboard(dispPtr->display, Tk_WindowId(tkwin),
  221.         False, GrabModeAsync, GrabModeAsync, TkCurrentTime(dispPtr));
  222.     if (grabResult != 0) {
  223.         XUngrabPointer(dispPtr->display, TkCurrentTime(dispPtr));
  224.         goto grabError;
  225.     }
  226.     dispPtr->grabFlags |= GRAB_GLOBAL;
  227.     } else {
  228.     /*
  229.      * The call to XUngrabPointer below is needed to release any
  230.      * existing auto-grab due to a button press.  This is needed
  231.      * so that local grabs behave the same as global grabs (the
  232.      * button grab is released by the X server in a global grab).
  233.      */
  234.  
  235.     XUngrabPointer(dispPtr->display, TkCurrentTime(dispPtr));
  236.     grabRequest = LastKnownRequestProcessed(dispPtr->display);
  237.     dispPtr->grabFlags &= ~GRAB_GLOBAL;
  238.  
  239.     /*
  240.      * Since we're not telling the server about the grab, we have
  241.      * to generate Leave and Enter events to move the pointer from
  242.      * its current window to the grab window.
  243.      */
  244.  
  245.     MovePointer2(dispPtr->pointerWinPtr, winPtr, NotifyGrab);
  246.     }
  247.     dispPtr->grabWinPtr = winPtr;
  248.  
  249.     /*
  250.      * When a grab occurs, X generates Enter and Leave events to move
  251.      * the pointer from its current window to the grab window, even if
  252.      * the current window is in the grab tree.  We don't want these
  253.      * events getting through to the application if the current window
  254.      * is in the grab tree.  In order to eliminate the bogus events,
  255.      * process all pending events and filter out the bogus ones.
  256.      *
  257.      * Also, filter out the final enter event into the grab window in
  258.      * any case:  this event shouldn't be delivered until the mouse really
  259.      * moves into that window.
  260.      *
  261.      * The code below reads in all the pending events, filters out the bad
  262.      * ones, and then pushes back all the events that weren't filtered.
  263.      * Another alternative would be to simply process the events
  264.      * immediately rather than pushing them back again.  However, this
  265.      * tends to interfere with scripts since it causes pending events
  266.      * to be processed during the "grab" command.  The "grab" command
  267.      * might have been invoked in the middle of some computation where
  268.      * it's a bad idea to process new events.
  269.      */
  270.  
  271.     XSync(dispPtr->display, False);
  272.     numEvents = QLength(dispPtr->display);
  273.     if (numEvents == 0) {
  274.     return TCL_OK;
  275.     }
  276.     events = (XEvent *) ckalloc((unsigned) (numEvents * sizeof(XEvent)));
  277.     for (i = 0; i < numEvents; i++) {
  278.     XNextEvent(dispPtr->display, &events[i]);
  279.     }
  280.     inSequence = ignoring = 0;
  281.     for (i = numEvents-1, eventPtr = events; i >= 0; i--, eventPtr++) {
  282.     if (((eventPtr->type != EnterNotify)
  283.         && (eventPtr->type != LeaveNotify))
  284.         || (eventPtr->xcrossing.mode != NotifyGrab)) {
  285.         continue;
  286.     }
  287.  
  288.     /*
  289.      * The diff caculcation below is trickier than you might think,
  290.      * due to the fact that the event serial number is unsigned and
  291.      * serial numbers can wrap around.
  292.      */
  293.  
  294.     diff = eventPtr->xcrossing.serial;
  295.     diff -= grabRequest;
  296.     if (!inSequence && (diff >= 0)) {
  297.         /*
  298.          * This is the first event of the grab sequence.  See if its
  299.          * window is in the grab tree and ignore the sequence if it is.
  300.          */
  301.  
  302.         inSequence = 1;
  303.         if (XFindContext(dispPtr->display, eventPtr->xcrossing.window,
  304.             tkWindowContext, (caddr_t *) &winPtr2) == 0) {
  305.         for ( ; winPtr2 != NULL; winPtr2 = winPtr2->parentPtr) {
  306.             if (winPtr2 == dispPtr->grabWinPtr) {
  307.             ignoring = 1;
  308.             break;
  309.             }
  310.         }
  311.         }
  312.     }
  313.     if (ignoring) {
  314.         eventPtr->type = 0;
  315.     }
  316.     if (inSequence && (eventPtr->type == EnterNotify)
  317.         && (dispPtr->grabWinPtr->window
  318.         == eventPtr->xcrossing.window)) {
  319.         eventPtr->type = 0;
  320.         break;
  321.     }
  322.     }
  323.     for (i = numEvents-1, eventPtr = &events[i]; i >= 0; i--, eventPtr--) {
  324.     if (eventPtr->type != 0) {
  325.         XPutBackEvent(dispPtr->display, eventPtr);
  326.     }
  327.     }
  328.     ckfree((char *) events);
  329.     return TCL_OK;
  330. }
  331.  
  332. /*
  333.  *----------------------------------------------------------------------
  334.  *
  335.  * Tk_Ungrab --
  336.  *
  337.  *    Releases a grab on the mouse pointer and keyboard.
  338.  *
  339.  * Results:
  340.  *    None.
  341.  *
  342.  * Side effects:
  343.  *    Pointer and keyboard events will start being delivered to other
  344.  *    windows again.
  345.  *
  346.  *----------------------------------------------------------------------
  347.  */
  348.  
  349. void
  350. Tk_Ungrab(tkwin)
  351.     Tk_Window tkwin;            /* Window that identifies display
  352.                      * for grab to be released. */
  353. {
  354.     TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
  355.     int inSequence, ignoring, ungrabRequest, numEvents, i, j, diff;
  356.     TkWindow *grabWinPtr, *winPtr;
  357.     XEvent *events, *eventPtr, *eventPtr2;
  358.  
  359.     grabWinPtr = dispPtr->grabWinPtr;
  360.     if (grabWinPtr == NULL) {
  361.     return;
  362.     }
  363.     dispPtr->grabWinPtr = NULL;
  364.     dispPtr->buttonWinPtr = NULL;
  365.     if (dispPtr->grabFlags & GRAB_GLOBAL) {
  366.     ungrabRequest = NextRequest(dispPtr->display);
  367.     XUngrabPointer(dispPtr->display, TkCurrentTime(dispPtr));
  368.     XUngrabKeyboard(dispPtr->display, TkCurrentTime(dispPtr));
  369.     XSync(dispPtr->display, False);
  370.     } else {
  371.     ungrabRequest = LastKnownRequestProcessed(dispPtr->display);
  372.     if ((dispPtr->ungrabWinPtr != NULL)
  373.         && (dispPtr->ungrabWinPtr->mainPtr != grabWinPtr->mainPtr)) {
  374.  
  375.         /*
  376.          * Don't report entries down into a window of a different
  377.          * application, since it's already seen those entries earlier.
  378.          */
  379.  
  380.         dispPtr->ungrabWinPtr = NULL;
  381.     }
  382.     MovePointer2(grabWinPtr, dispPtr->ungrabWinPtr, NotifyUngrab);
  383.     }
  384.  
  385.     /*
  386.      * We have to filter all the pending events in a fashion similar to
  387.      * Tk_Grab. As with grabs, the X server generates an Enter-Leave event
  388.      * sequence to move the pointer from the grab window back to its
  389.      * current window.  We need to ignore this sequence if the pointer
  390.      * is being moved to a window that's already in the grab tree.
  391.      */
  392.  
  393.     numEvents = QLength(dispPtr->display);
  394.     if (numEvents == 0) {
  395.     return;
  396.     }
  397.     events = (XEvent *) ckalloc((unsigned) (numEvents * sizeof(XEvent)));
  398.     for (i = 0; i < numEvents; i++) {
  399.     XNextEvent(dispPtr->display, &events[i]);
  400.     }
  401.     inSequence = ignoring = 0;
  402.     for (i = numEvents-1, eventPtr = events; i >= 0; i--, eventPtr++) {
  403.     if (((eventPtr->type != EnterNotify)
  404.         && (eventPtr->type != LeaveNotify))
  405.         || (eventPtr->xcrossing.mode != NotifyUngrab)) {
  406.         continue;
  407.     }
  408.     diff = eventPtr->xcrossing.serial;
  409.     diff -= ungrabRequest;
  410.     if (!inSequence && (diff >= 0)) {
  411.  
  412.         /*
  413.          * This is the first event of the ungrab sequence.  Scan forward
  414.          * looking for the final Enter event in the sequence.  Then see
  415.          * if that event's window is in the grab tree.
  416.          */
  417.  
  418.         inSequence = 1;
  419.         for (j = i, eventPtr2 = eventPtr; j >= 0; j--, eventPtr2++) {
  420.         if (eventPtr2->type == EnterNotify) {
  421.             if (eventPtr2->xcrossing.mode != NotifyUngrab) {
  422.             break;
  423.             }
  424.             if ((eventPtr2->xcrossing.detail != NotifyAncestor)
  425.                 && (eventPtr2->xcrossing.detail != NotifyInferior)
  426.                 && (eventPtr2->xcrossing.detail
  427.                     != NotifyNonlinear)) {
  428.             continue;
  429.             }
  430.             if (XFindContext(dispPtr->display,
  431.                 eventPtr2->xcrossing.window,
  432.                 tkWindowContext, (caddr_t *) &winPtr) == 0) {
  433.             for ( ; winPtr != NULL; winPtr = winPtr->parentPtr) {
  434.                 if (winPtr == grabWinPtr) {
  435.                 ignoring = 1;
  436.                 break;
  437.                 }
  438.             }
  439.             }
  440.             break;
  441.         } else if ((eventPtr2->type != LeaveNotify)
  442.             || (eventPtr2->xcrossing.mode != NotifyUngrab)) {
  443.             break;
  444.         }
  445.         }
  446.     }
  447.     if (ignoring) {
  448.         eventPtr->type = 0;
  449.     }
  450.     }
  451.     for (i = numEvents-1, eventPtr = &events[i]; i >= 0; i--, eventPtr--) {
  452.     if (eventPtr->type != 0) {
  453.         XPutBackEvent(dispPtr->display, eventPtr);
  454.     }
  455.     }
  456.     ckfree((char *) events);
  457. }
  458.  
  459. /*
  460.  *----------------------------------------------------------------------
  461.  *
  462.  * TkPointerEvent --
  463.  *
  464.  *    This procedure is called for each pointer-related event, before
  465.  *    the event has been processed.  It does various things to make
  466.  *    grabs work correctly.
  467.  *
  468.  * Results:
  469.  *    If the return value is 1 it means the event should be processed
  470.  *    (event handlers should be invoked).  If the return value is 0
  471.  *    it means the event should be ignored in order to make grabs
  472.  *    work correctly.  Note:  the event may be modified by this procedure.
  473.  *
  474.  * Side effects:
  475.  *    Grab state information may be updated.
  476.  *
  477.  *----------------------------------------------------------------------
  478.  */
  479.  
  480. int
  481. TkPointerEvent(eventPtr, winPtr)
  482.     register XEvent *eventPtr;        /* Pointer to the event. */
  483.     TkWindow *winPtr;            /* Tk's information for window
  484.                      * where event was reported. */
  485. {
  486.     register TkWindow *winPtr2;
  487.     TkDisplay *dispPtr = winPtr->dispPtr;
  488.     int outsideGrabTree = 0;
  489.     int originalFlags;
  490.     int appGrabbed = 0;            /* Non-zero means event is being
  491.                      * reported to an application that is
  492.                      * affected by the grab. */
  493. #define ALL_BUTTONS \
  494.     (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask)
  495.     static unsigned int state[] = {
  496.     Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask
  497.     };
  498.  
  499.     /*
  500.      * Don't do any filtering on events generated by the event-sharing code.
  501.      */
  502.  
  503.     if (eventPtr == tkShareEventPtr) {
  504.     return 1;
  505.     }
  506.  
  507.     /*
  508.      * If a grab is in effect, see if the event is being reported to
  509.      * a window in the grab tree.  Also see if the event is being reported
  510.      * to an application that is affected by the grab.
  511.      */
  512.  
  513.     if (dispPtr->grabWinPtr != NULL) {
  514.     if ((winPtr->mainPtr == dispPtr->grabWinPtr->mainPtr)
  515.         || (dispPtr->grabFlags & GRAB_GLOBAL)) {
  516.         appGrabbed = 1;
  517.     }
  518.     for (winPtr2 = winPtr; winPtr2 != dispPtr->grabWinPtr;
  519.         winPtr2 = winPtr2->parentPtr) {
  520.         if (winPtr2 == NULL) {
  521.         outsideGrabTree = 1;
  522.         break;
  523.         }
  524.     }
  525.     }
  526.  
  527.     originalFlags = dispPtr->grabFlags;
  528.     dispPtr->grabFlags &= ~GRAB_BUTTON_RELEASE;
  529.     if ((eventPtr->type == EnterNotify) || (eventPtr->type == LeaveNotify)) {
  530.     if ((eventPtr->type == EnterNotify)
  531.         && (eventPtr->xcrossing.detail != NotifyVirtual)
  532.         && (eventPtr->xcrossing.detail != NotifyNonlinearVirtual)) {
  533.         if ((dispPtr->grabWinPtr == NULL)
  534.             || (dispPtr->grabWinPtr->mainPtr == winPtr->mainPtr)) {
  535.         dispPtr->ungrabWinPtr = winPtr;
  536.         }
  537.         dispPtr->serverWinPtr = winPtr;
  538.     } else {
  539.         dispPtr->serverWinPtr = NULL;
  540.     }
  541.     if (dispPtr->grabWinPtr != NULL) {
  542.         if (eventPtr->xcrossing.mode == NotifyNormal) {
  543.         /*
  544.          * When a grab is active, X continues to report enter and
  545.          * leave events for windows outside the tree of the grab
  546.          * window.  Detect these events and ignore them.
  547.          */
  548.  
  549.         if (outsideGrabTree && appGrabbed) {
  550.             return 0;
  551.         }
  552.     
  553.         /*
  554.          * Make buttons have the same grab-like behavior inside a grab
  555.          * as they do outside a grab:  do this by ignoring enter and
  556.          * leave events except for the window in which the button was
  557.          * pressed.
  558.          */
  559.  
  560.         if ((dispPtr->buttonWinPtr != NULL)
  561.             && (winPtr != dispPtr->buttonWinPtr)) {
  562.             return 0;
  563.         }
  564.         } else if (eventPtr->xcrossing.mode == NotifyUngrab) {
  565.         /*
  566.          * Keep the GRAB_BUTTON_RELEASE flag on if it used to be on.
  567.          */
  568.  
  569.         dispPtr->grabFlags = originalFlags;
  570.         if (outsideGrabTree && appGrabbed
  571.             && (dispPtr->grabFlags & GRAB_BUTTON_RELEASE)) {
  572.             /*
  573.              * The only way we get here is if a button was pressed,
  574.              * then moved to a different window and released.  Enter
  575.              * and leave events were deferred while the button was
  576.              * down, but now we're getting them to move the pointer
  577.              * back to the right window, and this particular event
  578.              * is for a window outside the grab tree.  Ignore it.
  579.              */
  580.     
  581.             return 0;
  582.         }
  583.         }
  584.     }
  585.  
  586.     /*
  587.      * Keep track of the window containing the mouse, in order to
  588.      * detect various bogus event sequences.
  589.      */
  590.  
  591.     dispPtr->pointerWinPtr = dispPtr->serverWinPtr;
  592.     return 1;
  593.     }
  594.     if ((dispPtr->grabWinPtr == NULL) || !appGrabbed) {
  595.     return 1;
  596.     }
  597.  
  598.     if (eventPtr->type == MotionNotify) {
  599.     /*
  600.      * When grabs are active, X reports motion events relative to the
  601.      * window under the pointer.  Instead, it should report the events
  602.      * relative to the window the button went down in, if there is a
  603.      * button down.  Otherwise, if the pointer window is outside the
  604.      * subtree of the grab window, the events should be reported
  605.      * relative to the grab window.  Otherwise, the event should be
  606.      * reported to the pointer window.
  607.      */
  608.  
  609.     winPtr2 = winPtr;
  610.     if (dispPtr->buttonWinPtr != NULL) {
  611.         winPtr2 = dispPtr->buttonWinPtr;
  612.     } else if (outsideGrabTree || (dispPtr->serverWinPtr == NULL)) {
  613.         winPtr2 = dispPtr->grabWinPtr;
  614.     }
  615.     if (winPtr2 != winPtr) {
  616.         XEvent newEvent;
  617.  
  618.         newEvent = *eventPtr;
  619.         ChangeEventWindow(&newEvent, winPtr2);
  620.         XPutBackEvent(winPtr2->display, &newEvent);
  621.         return 0;
  622.     }
  623.     return 1;
  624.     }
  625.  
  626.     /*
  627.      * Process ButtonPress and ButtonRelease events:
  628.      * 1. Keep track of whether a button is down and what window it
  629.      *    went down in.
  630.      * 2. If the first button goes down outside the grab tree, pretend
  631.      *    it went down in the grab window.  Note: it's important to
  632.      *    redirect events to the grab window like this in order to make
  633.      *    things like menus work, where button presses outside the
  634.      *    grabbed menu need to be seen.  An application can always
  635.      *    ignore the events if they occur outside its window.
  636.      * 3. If a button press or release occurs outside the window where
  637.      *    the first button was pressed, retarget the event so it's reported
  638.      *    to the window where the first button was pressed.
  639.      * 4. If the last button is released in a window different than where
  640.      *    the first button was pressed, generate Enter/Leave events to
  641.      *    move the mouse from the button window to its current window.
  642.      * 5. If the grab is set at a time when a button is already down, or
  643.      *    if the window where the button was pressed was deleted, then
  644.      *    dispPtr->buttonWinPtr will stay NULL.  Just forget about the
  645.      *    auto-grab for the button press;  events will go to whatever
  646.      *    window contains the pointer.  If this window isn't in the grab
  647.      *    tree then redirect events to the grab window.
  648.      */
  649.  
  650.     if ((eventPtr->type == ButtonPress) || (eventPtr->type == ButtonRelease)) {
  651.     winPtr2 = dispPtr->buttonWinPtr;
  652.     if (winPtr2 == NULL) {
  653.         if (outsideGrabTree) {
  654.         winPtr2 = dispPtr->grabWinPtr;            /* Note 5. */
  655.         } else {
  656.         winPtr2 = winPtr;                /* Note 5. */
  657.         }
  658.     }
  659.     if (eventPtr->type == ButtonPress) {
  660.         if ((eventPtr->xbutton.state & ALL_BUTTONS) == 0) {
  661.         if (outsideGrabTree) {
  662.             XEvent newEvent;
  663.  
  664.             newEvent = *eventPtr;
  665.             ChangeEventWindow(&newEvent, dispPtr->grabWinPtr);
  666.             XPutBackEvent(dispPtr->display, &newEvent);
  667.             return 0;                    /* Note 2. */
  668.         }
  669.         dispPtr->buttonWinPtr = winPtr;
  670.         return 1;
  671.         }
  672.     } else {
  673.         if ((eventPtr->xbutton.state & ALL_BUTTONS)
  674.             == state[eventPtr->xbutton.button - Button1]) {
  675.         if ((dispPtr->buttonWinPtr != winPtr)
  676.             && (dispPtr->buttonWinPtr != NULL)) {
  677.             XEvent newEvent;                /* Note 4. */
  678.  
  679.             /*
  680.              * If the button release is made with pointer outside
  681.              * all applications, X reports it relative to the grab
  682.              * window.   Change the current window to NULL to
  683.              * reflect that the pointer's outside everything.  Do
  684.              * the same if the pointer's in a window that's not
  685.              * part of the grab tree.
  686.              */
  687.  
  688.             if (outsideGrabTree || (dispPtr->serverWinPtr == NULL)) {
  689.             winPtr = NULL;
  690.             }
  691.             newEvent = *eventPtr;
  692.             newEvent.xcrossing.mode = NotifyUngrab;
  693.             newEvent.xcrossing.focus = False;
  694.             newEvent.xcrossing.state =
  695.                 eventPtr->xbutton.state & ~ALL_BUTTONS;
  696.             MovePointer(&newEvent, dispPtr->buttonWinPtr, winPtr);
  697.         }
  698.         dispPtr->buttonWinPtr = NULL;
  699.         dispPtr->grabFlags |= GRAB_BUTTON_RELEASE;
  700.         }
  701.     }
  702.     if (winPtr2 != winPtr) {
  703.         XEvent newEvent;
  704.  
  705.         newEvent = *eventPtr;
  706.         ChangeEventWindow(&newEvent, winPtr2);
  707.         XPutBackEvent(dispPtr->display, &newEvent);
  708.         return 0;                        /* Note 3. */
  709.     }
  710.     }
  711.  
  712.     return 1;
  713. }
  714.  
  715. /*
  716.  *----------------------------------------------------------------------
  717.  *
  718.  * ChangeEventWindow --
  719.  *
  720.  *    Given an event and a new window to which the event should be
  721.  *    retargeted, modify fields of the event so that the event is
  722.  *    properly retargeted to the new window.
  723.  *
  724.  * Results:
  725.  *    The following fields of eventPtr are modified:  window,
  726.  *    subwindow, x, y, same_screen.
  727.  *
  728.  * Side effects:
  729.  *    None.
  730.  *
  731.  *----------------------------------------------------------------------
  732.  */
  733.  
  734. static void
  735. ChangeEventWindow(eventPtr, winPtr)
  736.     register XEvent *eventPtr;    /* Event to retarget.  Must have
  737.                  * type ButtonPress, ButtonRelease, KeyPress,
  738.                  * KeyRelease, MotionNotify, EnterNotify,
  739.                  * or LeaveNotify. */
  740.     TkWindow *winPtr;        /* New target window for event. */
  741. {
  742.     int x, y, sameScreen, bd;
  743.     register TkWindow *childPtr;
  744.  
  745.     eventPtr->xmotion.window = Tk_WindowId(winPtr);
  746.     if (eventPtr->xmotion.root ==
  747.         RootWindow(winPtr->display, winPtr->screenNum)) {
  748.     Tk_GetRootCoords((Tk_Window) winPtr, &x, &y);
  749.     eventPtr->xmotion.x = eventPtr->xmotion.x_root - x;
  750.     eventPtr->xmotion.y = eventPtr->xmotion.y_root - y;
  751.     eventPtr->xmotion.subwindow = None;
  752.     for (childPtr = winPtr->childList; childPtr != NULL;
  753.         childPtr = childPtr->nextPtr) {
  754.         if (childPtr->flags & TK_TOP_LEVEL) {
  755.         continue;
  756.         }
  757.         x = eventPtr->xmotion.x - childPtr->changes.x;
  758.         y = eventPtr->xmotion.y - childPtr->changes.y;
  759.         bd = childPtr->changes.border_width;
  760.         if ((x >= -bd) && (y >= -bd)
  761.             && (x < (childPtr->changes.width + bd))
  762.             && (y < (childPtr->changes.width + bd))) {
  763.         eventPtr->xmotion.subwindow = childPtr->window;
  764.         }
  765.     }
  766.     sameScreen = 1;
  767.     } else {
  768.     eventPtr->xmotion.x = 0;
  769.     eventPtr->xmotion.y = 0;
  770.     eventPtr->xmotion.subwindow = None;
  771.     sameScreen = 0;
  772.     }
  773.     if (eventPtr->type == MotionNotify) {
  774.     eventPtr->xmotion.same_screen = sameScreen;
  775.     } else {
  776.     eventPtr->xbutton.same_screen = sameScreen;
  777.     }
  778. }
  779.  
  780. /*
  781.  *----------------------------------------------------------------------
  782.  *
  783.  * MovePointer --
  784.  *
  785.  *    This procedure synthesizes EnterNotify and LeaveNotify events
  786.  *    to correctly transfer the pointer from one window to another.
  787.  *
  788.  * Results:
  789.  *    None.
  790.  *
  791.  * Side effects:
  792.  *    Synthesized events may be pushed back onto the event queue.
  793.  *    The event pointed to by eventPtr is modified.
  794.  *
  795.  *----------------------------------------------------------------------
  796.  */
  797.  
  798. static void
  799. MovePointer(eventPtr, sourcePtr, destPtr)
  800.     XEvent *eventPtr;        /* A template X event.  Must have all fields
  801.                  * properly set for EnterNotify and LeaveNotify
  802.                  * events except window, subwindow, x, y,
  803.                  * detail, and same_screen.  (x_root and y_root
  804.                  * must be valid, even though x and y needn't
  805.                  * be valid). */
  806.     TkWindow *sourcePtr;    /* Window currently containing pointer (NULL
  807.                  * means it's not one managed by this
  808.                  * process). */
  809.     TkWindow *destPtr;        /* Window that is to end up containing the
  810.                  * pointer (NULL means it's not one managed
  811.                  * by this process). */
  812. {
  813.     TkDisplay *dispPtr;
  814.     register TkWindow *ancestorPtr;    /* Lowest ancestor shared between
  815.                      * sourcePtr and destPtr, or
  816.                      * sourcePtr's top-level window if no
  817.                      * shared ancestor. */
  818.     register TkWindow *winPtr;
  819.     int upLevels, downLevels, i, j;
  820.  
  821.     /*
  822.      * There are four possible cases to deal with:
  823.      *
  824.      * 1. SourcePtr and destPtr are the same.  There's nothing to do in
  825.      *    this case.
  826.      * 2. SourcePtr is an ancestor of destPtr in the same top-level
  827.      *    window.  Must generate events down the window tree from source
  828.      *    to dest.
  829.      * 3. DestPtr is an ancestor of sourcePtr in the same top-level
  830.      *    window.  Must generate events up the window tree from sourcePtr
  831.      *    to destPtr.
  832.      * 4. All other cases.  Must first generate events up the window tree
  833.      *    from sourcePtr to its top-level, then down from destPtr's
  834.      *    top-level to destPtr. This form is called "non-linear."
  835.      *
  836.      * The code below separates these four cases and decides how many levels
  837.      * up and down events have to be generated for.
  838.      */
  839.  
  840.     if (sourcePtr == destPtr) {
  841.     return;
  842.     }
  843.  
  844.     /*
  845.      * Mark destPtr and all of its ancestors with a special flag bit.
  846.      */
  847.  
  848.     if (destPtr != NULL) {
  849.     dispPtr = destPtr->dispPtr;
  850.     for (winPtr = destPtr; ; winPtr = winPtr->parentPtr) {
  851.         winPtr->flags |= TK_GRAB_FLAG;
  852.         if (winPtr->flags & TK_TOP_LEVEL) {
  853.         break;
  854.         }
  855.     }
  856.     } else {
  857.     dispPtr = sourcePtr->dispPtr;
  858.     }
  859.  
  860.     /*
  861.      * Search upwards from sourcePtr until an ancestor of destPtr is
  862.      * found or a top-level window is reached.  Remember if we pass out
  863.      * of the grab tree along the way, since this means we'll have to
  864.      * skip some of the events that would otherwise be generated.
  865.      */
  866.  
  867.     ancestorPtr = sourcePtr;
  868.     upLevels = 0;
  869.     if (sourcePtr != NULL) {
  870.     for (; ; upLevels++, ancestorPtr = ancestorPtr->parentPtr) {
  871.         if (ancestorPtr->flags & TK_GRAB_FLAG) {
  872.         break;
  873.         }
  874.         if (ancestorPtr->flags & TK_TOP_LEVEL)  {
  875.         upLevels++;
  876.         break;
  877.         }
  878.     }
  879.     }
  880.  
  881.     /*
  882.      * Search upwards from destPtr again, clearing the flag bits and
  883.      * remembering how many levels up we had to go.
  884.      */
  885.  
  886.     if (destPtr == NULL) {
  887.     downLevels = 0;
  888.     } else {
  889.     downLevels = -1;
  890.     for (i = 0, winPtr = destPtr; ; i++, winPtr = winPtr->parentPtr) {
  891.         winPtr->flags &= ~TK_GRAB_FLAG;
  892.         if (winPtr == ancestorPtr) {
  893.         downLevels = i;
  894.         }
  895.         if (winPtr->flags & TK_TOP_LEVEL) {
  896.         if (downLevels == -1) {
  897.             downLevels = i+1;
  898.         }
  899.         break;
  900.         }
  901.     }
  902.     }
  903.  
  904.     /*
  905.      * Generate enter/leave events and push them back onto the event
  906.      * queue.  This has to be done backwards, since the last event
  907.      * pushed will be the first one processed.
  908.      */
  909.  
  910. #define PUSH_EVENT(w, t, d)            \
  911.     if (w->window != None) {            \
  912.     eventPtr->type = t;            \
  913.     eventPtr->xcrossing.detail = d;        \
  914.     ChangeEventWindow(eventPtr, w);        \
  915.     XPutBackEvent(w->display, eventPtr);    \
  916.     }
  917.  
  918.     if (downLevels == 0) {
  919.     
  920.     /*
  921.      * SourcePtr is an inferior of destPtr.
  922.      */
  923.  
  924.     if (destPtr != NULL) {
  925.         PUSH_EVENT(destPtr, EnterNotify, NotifyInferior);
  926.     }
  927.     for (i = upLevels-1; i > 0; i--) {
  928.         for (winPtr = sourcePtr, j = 0; j < i;
  929.             winPtr = winPtr->parentPtr, j++) {
  930.         if (winPtr == dispPtr->grabWinPtr) {
  931.             goto nextIteration;
  932.         }
  933.         }
  934.         PUSH_EVENT(winPtr, LeaveNotify, NotifyVirtual);
  935.         nextIteration: continue;
  936.     }
  937.     PUSH_EVENT(sourcePtr, LeaveNotify, NotifyAncestor);
  938.     } else if (upLevels == 0) {
  939.  
  940.     /*
  941.      * DestPtr is an inferior of sourcePtr.
  942.      */
  943.  
  944.     if (destPtr != NULL) {
  945.         PUSH_EVENT(destPtr, EnterNotify, NotifyAncestor);
  946.     }
  947.     for (winPtr = destPtr->parentPtr, i = downLevels-1; i > 0;
  948.         winPtr = winPtr->parentPtr, i--) {
  949.         PUSH_EVENT(winPtr, EnterNotify, NotifyVirtual);
  950.     }
  951.     if (sourcePtr != NULL) {
  952.         PUSH_EVENT(sourcePtr, LeaveNotify, NotifyInferior);
  953.     }
  954.     } else {
  955.  
  956.     /*
  957.      * Non-linear:  neither window is an inferior of the other.
  958.      */
  959.  
  960.     if (destPtr != NULL) {
  961.         PUSH_EVENT(destPtr, EnterNotify, NotifyNonlinear);
  962.     }
  963.     if (destPtr != dispPtr->grabWinPtr) {
  964.         for (winPtr = destPtr->parentPtr, i = downLevels-1; i > 0;
  965.             winPtr = winPtr->parentPtr, i--) {
  966.         PUSH_EVENT(winPtr, EnterNotify, NotifyNonlinearVirtual);
  967.         if (winPtr == dispPtr->grabWinPtr) {
  968.             break;
  969.         }
  970.         }
  971.     }
  972.     for (i = upLevels-1; i > 0; i--) {
  973.         for (winPtr = sourcePtr, j = 0; j < i;
  974.             winPtr = winPtr->parentPtr, j++) {
  975.         if (winPtr == dispPtr->grabWinPtr) {
  976.             goto nextWindow;
  977.         }
  978.         }
  979.         PUSH_EVENT(winPtr, LeaveNotify, NotifyNonlinearVirtual);
  980.         nextWindow: continue;
  981.     }
  982.     PUSH_EVENT(sourcePtr, LeaveNotify, NotifyNonlinear);
  983.     }
  984. }
  985.  
  986. /*
  987.  *----------------------------------------------------------------------
  988.  *
  989.  * MovePointer2 --
  990.  *
  991.  *    This procedure synthesizes  EnterNotify and LeaveNotify events
  992.  *    to correctly transfer the pointer from one window to another.
  993.  *    It is different from MovePointer in that no template X event
  994.  *    needs to be supplied;  this procedure generates the template
  995.  *    event and calls MovePointer.
  996.  *
  997.  * Results:
  998.  *    None.
  999.  *
  1000.  * Side effects:
  1001.  *    Synthesized events may be pushed back onto the event queue.
  1002.  *
  1003.  *----------------------------------------------------------------------
  1004.  */
  1005.  
  1006. static void
  1007. MovePointer2(sourcePtr, destPtr, mode)
  1008.     TkWindow *sourcePtr;    /* Window currently containing pointer (NULL
  1009.                  * means it's not one managed by this
  1010.                  * process). */
  1011.     TkWindow *destPtr;        /* Window that is to end up containing the
  1012.                  * pointer (NULL means it's not one managed
  1013.                  * by this process). */
  1014.     int mode;            /* Mode for enter/leave events, such as
  1015.                  * NotifyNormal or NotifyUngrab. */
  1016. {
  1017.     XEvent event;
  1018.     Window dummy1, dummy2;
  1019.     int dummy3, dummy4;
  1020.     TkWindow *winPtr;
  1021.  
  1022.     winPtr = sourcePtr;
  1023.     if ((winPtr == NULL) || (winPtr->window == None)) {
  1024.     winPtr = destPtr;
  1025.     if ((winPtr == NULL) || (winPtr->window == None)) {
  1026.         return;
  1027.     }
  1028.     }
  1029.  
  1030.     event.xcrossing.serial = LastKnownRequestProcessed(winPtr->display);
  1031.     event.xcrossing.send_event = False;
  1032.     event.xcrossing.display = winPtr->display;
  1033.     event.xcrossing.root = RootWindow(winPtr->display, winPtr->screenNum);
  1034.     event.xcrossing.time = TkCurrentTime(winPtr->dispPtr);
  1035.     XQueryPointer(winPtr->display, winPtr->window, &dummy1, &dummy2,
  1036.         &event.xcrossing.x_root, &event.xcrossing.y_root,
  1037.         &dummy3, &dummy4, &event.xcrossing.state);
  1038.     event.xcrossing.mode = mode;
  1039.     event.xcrossing.focus = False;
  1040.     MovePointer(&event, sourcePtr, destPtr);
  1041. }
  1042.  
  1043. /*
  1044.  *----------------------------------------------------------------------
  1045.  *
  1046.  * TkGrabDeadWindow --
  1047.  *
  1048.  *    This procedure is invoked whenever a window is deleted, so that
  1049.  *    grab-related cleanup can be performed.
  1050.  *
  1051.  * Results:
  1052.  *    None.
  1053.  *
  1054.  * Side effects:
  1055.  *    Various cleanups happen, such as generating events to move the
  1056.  *    pointer back to its "natural" window as if an ungrab had been
  1057.  *    done.  See the code.
  1058.  *
  1059.  *----------------------------------------------------------------------
  1060.  */
  1061.  
  1062. void
  1063. TkGrabDeadWindow(winPtr)
  1064.     register TkWindow *winPtr;        /* Window that is in the process
  1065.                      * of being deleted. */
  1066. {
  1067.     TkDisplay *dispPtr = winPtr->dispPtr;
  1068.  
  1069.     if (dispPtr->grabWinPtr == winPtr) {
  1070.     dispPtr->grabWinPtr = NULL;
  1071.     if (!(dispPtr->grabFlags & GRAB_GLOBAL)) {
  1072.         /*
  1073.          * Must generate enter/leave events to move back to the window
  1074.          * that contains the mouse pointer.  We needn't filter events
  1075.          * here like we do in Tk_Ungrab because there are no children
  1076.          * of the grab window left in existence.
  1077.          */
  1078.  
  1079.         movePointerBack:
  1080.         if ((dispPtr->ungrabWinPtr != NULL)
  1081.             && (dispPtr->ungrabWinPtr->mainPtr != winPtr->mainPtr)) {
  1082.         dispPtr->ungrabWinPtr = NULL;
  1083.         }
  1084.         MovePointer2(winPtr, dispPtr->ungrabWinPtr, NotifyUngrab);
  1085.     }
  1086.     } else if (dispPtr->buttonWinPtr == winPtr) {
  1087.     /*
  1088.      * The window in which a button was pressed was deleted.  Simulate
  1089.      * dropping the button auto-grab by generating Enter and Leave
  1090.      * events to move the pointer back to the window it's really on
  1091.      * top of.
  1092.      */
  1093.  
  1094.     dispPtr->buttonWinPtr = NULL;
  1095.     goto movePointerBack;
  1096.     }
  1097.     if (dispPtr->ungrabWinPtr == winPtr) {
  1098.     dispPtr->ungrabWinPtr = NULL;
  1099.     }
  1100.     if (dispPtr->pointerWinPtr == winPtr) {
  1101.     dispPtr->pointerWinPtr = NULL;
  1102.     }
  1103.     if (dispPtr->serverWinPtr == winPtr) {
  1104.     dispPtr->serverWinPtr = NULL;
  1105.     }
  1106. }
  1107.